package whf.framework.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import whf.framework.entity.Entity;
import whf.framework.exception.CreateException;
import whf.framework.exception.FindException;
import whf.framework.exception.NotFoundException;
import whf.framework.exception.RemoveException;
import whf.framework.exception.UpdateException;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.meta.Meta;
import whf.framework.meta.MetaFactory;
import whf.framework.meta.entity.IProperty;
import whf.framework.service.SpringService;
import whf.framework.util.BeanUtils;
import whf.framework.util.DateUtils;
import whf.framework.util.StringUtils;
import whf.framework.util.TypeUtils;
import whf.framework.util.ThreadContext;

/**
 * 实现接口IDAO所提供的各种操作对象的方法
 * 
 * @author wanghaifeng
 * @version 1.0
 */
@SuppressWarnings(value={"unchecked"})
public abstract class DAOImp<T> extends SpringService implements DAO<T>{
	private static Log log = LogFactory.getLog(DAOImp.class);
	
	private DAOSupport<T> daoSupport;

	private Meta meta = null;

	public void setDaoSupport(DAOSupport<T> daoSupport) {
		this.daoSupport = daoSupport;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#getMeta()
	 */
	public Meta getMeta(){
		if(this.meta == null) {
			meta = MetaFactory.findByDAOClass(this.getClass());
		}
		return this.meta;
	}
	
	/**
	 * 重构hql语句，如果hql中没有from字句则增加上
	 * @modify wanghaifeng Aug 25, 2006 1:07:53 PM
	 * @param source
	 * @return
	 */
	protected String buildQueryString(String source){
		if(source == null) source = "";
		source = source.trim();
		//
		StringBuilder sql = new StringBuilder();
		if(!source.toLowerCase().startsWith("select ") && !source.toLowerCase().startsWith("from ")){	//如果sql语句不是使用这个开头
			sql.append(" from ").append(getMeta().getModelClass().getName()).append(" t ");
			if(!source.startsWith("order by ") && !source.startsWith("where ") && !StringUtils.isEmpty(source)){
				sql.append(" where ");
			}
			sql.append(source);
		} else {
			sql.append(source); //增加原语句中的from
		}
		log.debug(this.getClass().getSimpleName()+".buildQueryString:\t"+sql);
		return sql.toString();
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findAll()
	 */
	public List<T> findAll() throws FindException{
		return this.find(this.buildQueryString(""));
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findAll(int, int)
	 */
	public List<T> findAll(int start, int pageSize) throws FindException{
		return list(this.buildQueryString(""),new Object[]{},start,pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#create(whf.framework.entity.IBaseBO)
	 */
	public void create(T entity) throws CreateException {
		try {
			if(entity instanceof Entity) {
				((Entity)entity).setCreateDate(DateUtils.getNow());
				if(((Entity)entity).getCreateBy() != null && ThreadContext.getUserContext() != null){
					((Entity)entity).setCreateBy(ThreadContext.getUserInUserContext());
				}
			}
			this.daoSupport.save(entity);
		} catch (Exception e) {
			log.debug(this, e);
			throw new CreateException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findByPrimaryKey(java.io.Serializable)
	 */
	public T findByPrimaryKey(Serializable key) throws NotFoundException {
		if (key == null)
			throw new NotFoundException(key+"");
		try {
			T entity = this.daoSupport.load(getMeta().getModelClass(), key);
			if(entity == null){
				throw new NotFoundException("The key is:"+key);
			}else{
				return entity;
			}
		} catch (Exception e) {
			log.debug(this, e);
			NotFoundException nfe = new NotFoundException(e);
			nfe.setKey(key);
			throw nfe;
		}
	}
	
	public T findByCode(String code) throws FindException {
		List<T> tmp = this.find("t.code=", code);
		if(tmp.size() > 0)
			return tmp.get(0);
		else
			return null;
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findByPrimaryKeyWithoutLazy(java.io.Serializable)
	 */
	public T findByPrimaryKeyWithoutLazy(Serializable key) throws NotFoundException{
		if (key == null)
			throw new NotFoundException(key+"");
		try {
			T entity = this.daoSupport.loadWithoutLazy(getMeta().getModelClass(), key);
			if(entity == null){
				throw new NotFoundException("The key is:"+key);
			}else{
				return entity;
			}
		} catch (Exception e) {
			log.debug(this, e);
			NotFoundException nfe = new NotFoundException(e);
			nfe.setKey(key);
			throw nfe;
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#update(whf.framework.entity.IBaseBO)
	 */
	public void update(T entity) throws UpdateException {
		try {
			if(entity instanceof Entity) {
				((Entity)entity).setUpdateDate(DateUtils.getNow());
				if(((Entity)entity).getUpdateBy() != null && ThreadContext.getUserContext() != null){
					((Entity)entity).setUpdateBy(ThreadContext.getUserInUserContext());
				}
			}
			this.daoSupport.modify(entity);
		} catch (Exception e) {
			log.debug(this, e);
			throw new UpdateException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#remove(whf.framework.entity.IBaseBO)
	 */
	public void remove(T entity) throws RemoveException {
		try {
			if (entity == null)
				return;
			this.daoSupport.delete(entity);
		} catch (Exception e) {
			log.debug(this, e);
			throw new RemoveException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#getRowCount(java.lang.String)
	 */
	public int getRowCount(String whereString) throws FindException {
		String sql = this.buildQueryString(whereString);
		sql = StringUtils.replace(sql," ","  ");
		int orderIndex = sql.toLowerCase().indexOf("order by");
		if(orderIndex > 0){
			sql = sql.substring(0, orderIndex );
		}
		sql = "select count(*) " + sql;
		List list = find(sql, new Object[]{});
		if(list.size() > 0){
			Object rowObj = list.get(0);
			if(rowObj instanceof Long){
				return ((Long)rowObj).intValue();
			} else {
				return ((Long)rowObj).intValue();
			}
		}else{
			return 0;
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#getRowCount(java.lang.String, java.lang.Object[])
	 */
	public int getRowCount(String whereString, Object[] params)
			throws FindException {
		try {
			String sql = this.buildQueryString(whereString);
			sql = StringUtils.replace(sql," ","  ");
			int orderIndex = sql.toLowerCase().indexOf("order by");
			if(orderIndex > 0){
				sql = sql.substring(0, orderIndex );
			}
			sql = "select count(*) " + sql;
			//
			List list = find(sql, params);
			if(list.size() == 0){
				return 0;
			}else{
				Object rowObj = list.get(0);
				if(rowObj instanceof Long)
					return ((Long)rowObj).intValue();
				else
					return ((Integer)rowObj).intValue();
			}
		} catch (Exception e) {
			log.debug(this, e);
			throw new FindException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#find(java.lang.String)
	 */
	public List<T> find(String query) throws FindException {
		try {
			return this.daoSupport.findWithListResult(this.buildQueryString(query));
		} catch (Exception e) {
			log.debug(this, e);
			throw new FindException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#find(java.lang.String, java.lang.Object)
	 */
	public List<T> find(String query, Object arg) throws FindException {
		try {
			return this.daoSupport.findWithListResult(this.buildQueryString(query), arg);
		} catch (Exception e) {
			log.debug(this, e);
			throw new FindException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#find(java.lang.String, java.lang.Object[])
	 */
	public List<T> find(String query, Object[] args) throws FindException {
		try {
			return this.daoSupport.findWithListResult(this.buildQueryString(query), args);
		} catch (Exception e) {
			log.debug(this, e);
			throw new FindException(e);
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findBySql(java.lang.String, int, int)
	 */
	public List<T> findBySql(String sql, int start, int pageSize) throws FindException {
		try{
			Meta meta = MetaFactory.findByDAOClass(this.getClass());
			StringBuilder sb = new StringBuilder("select ");
			boolean hasField = false;
			for(IProperty p: meta.getProperties()){
				String propName = p.getName();
				String colName = p.getDbColumnName();
				Class propType = p.getDataTypeClass();
				log.debug(propName);
				log.debug(propType);
				log.debug(colName);
				if(StringUtils.isEmpty(propName) || StringUtils.isEmpty(colName) 
						|| !TypeUtils.isPrimitiveType(propType)){
					continue;
				} else {
					sb.append(colName).append(" as {t.").append(propName).append("},");
					hasField = true;
				}
			}
			if(hasField){
				sb.deleteCharAt(sb.length() - 1);
			} else {
				sb.append(" {t.*} ");
			}
			sb.append(" from ").append(meta.getPersistenceTable()).append(" t ");
			if(!StringUtils.isEmpty(sql)){
				if(sql.toLowerCase().indexOf("where") < 0){
					sb.append(" where ").append(sql);
				} else {
					sb.append(sql);
				}
			}
			return this.daoSupport.findBySql(meta.getModelClass(), sb.toString(), null, null, start, pageSize);
		}catch(Exception e){
			if(e instanceof FindException){
				throw (FindException)e;
			} else {
				throw new FindException(e);
			}
		}
	}

	//-------------------- Query functions begin ---------------------
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#query(java.lang.String, int, int)
	 */
	public List<T> query(String queryString, int start, int pageSize) throws FindException {
		return list(this.buildQueryString(queryString), start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#query(java.lang.String)
	 */
	public List<T> query(String queryString) throws FindException {
		return query(queryString, 0, Integer.MAX_VALUE);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#queryWithoutLazy(java.lang.String)
	 */
	public List<T> queryWithoutLazy(String queryString) throws FindException{
		return queryWithoutLazy(queryString, 0, Integer.MAX_VALUE);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#queryWithoutLazy(java.lang.String, int, int)
	 */
	public List<T> queryWithoutLazy(String queryString, int start, int pageSize) throws FindException{
		return this.daoSupport.findWithoutLazy(this.buildQueryString(queryString), start, pageSize);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#getQueryRowCount(java.lang.String)
	 */
	public int getQueryRowCount(String queryString) throws FindException{
		return this.getRowCount(queryString);
	}
	//-------------------- Query functions end ---------------------
	
	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findByProperty(java.util.Map, int, int)
	 */
	public List<T> findByProperties(Map<String, Object> properties, int start, int pageSize) throws FindException{
		int paramCount = 0;
		Class cls = this.getMeta().getModelClass();
		for(String propertyName: properties.keySet()){
			if(!BeanUtils.hasProperty(cls, propertyName)){
				properties.remove(propertyName);
				continue;
			}
			Object value = properties.get(propertyName);
			if(value instanceof Collection){
				paramCount += ((Collection)value).size();
			} else {
				paramCount ++;
			}
		}
		Object[] params = new Object[paramCount];
		StringBuilder sb = new StringBuilder();
		if(properties.keySet().size() >0){
			int i = 0;
			for(String propertyName: properties.keySet()){
				Object value = properties.get(propertyName);
				if(i>0) sb.append(" and ");
				if(value instanceof Collection){
					sb.append("(");
					for(Object val : ((Collection)value)){
						if(!sb.toString().endsWith("{")) sb.append(" or ");
						params[i] = val;
						sb.append("? in t.").append(propertyName);
						i++;
					}
					sb.append(")");
					continue;
				} else {
					params[i] = value;
				}
				sb.append(" t.").append(propertyName).append(" = ? ");
				i++;
			}
		}
		return this.list(sb.toString(), params,start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findByPropertiesWithoutLazy(java.util.Map, int, int)
	 */
	public List<T> findByPropertiesWithoutLazy(Map<String, Object> properties, int start, int pageSize) throws FindException {
		int paramCount = 0;
		Class cls = this.getMeta().getModelClass();
		for(String propertyName: properties.keySet()){
			if(!BeanUtils.hasProperty(cls, propertyName)){
				properties.remove(propertyName);
				continue;
			}
			Object value = properties.get(propertyName);
			if(value instanceof Collection){
				paramCount += ((Collection)value).size();
			} else {
				paramCount ++;
			}
		}
		Object[] params = new Object[paramCount];
		StringBuilder sb = new StringBuilder();
		if(properties.keySet().size() >0){
			int i = 0;
			for(String propertyName: properties.keySet()){
				Object value = properties.get(propertyName);
				if(i>0) sb.append(" and ");
				if(value instanceof Collection){
					sb.append("(");
					for(Object val : ((Collection)value)){
						if(!sb.toString().endsWith("{")) sb.append(" or ");
						params[i] = val;
						sb.append("? in t.").append(propertyName);
						i++;
					}
					sb.append(")");
					continue;
				} else {
					params[i] = value;
				}
				sb.append(" t.").append(propertyName).append(" = ? ");
				i++;
			}
		}
		return this.findWithoutLazy(sb.toString(), params,start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String, int, int)
	 */
	public List<T> findWithoutLazy(String query, int start, int pageSize) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query), start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String, java.lang.Object, int, int)
	 */
	public List<T> findWithoutLazy(String query, Object arg, int start, int pageSize) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query), arg, start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String, java.lang.Object)
	 */
	public List<T> findWithoutLazy(String query, Object arg) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query), arg);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String, java.lang.Object[], int, int)
	 */
	public List<T> findWithoutLazy(String query, Object[] params, int start, int pageSize) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query), params, start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String, java.lang.Object[])
	 */
	public List<T> findWithoutLazy(String query, Object[] params) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query), params);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#findWithoutLazy(java.lang.String)
	 */
	public List<T> findWithoutLazy(String query) throws FindException {
		return this.daoSupport.findWithoutLazy(this.buildQueryString(query));
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#list(java.lang.String, int, int)
	 */
	public List<T> list(String query, int start, int pageSize) {
		return this.daoSupport.list(this.buildQueryString(query), start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#list(java.lang.String, java.lang.Object[], int, int)
	 */
	public List<T> list(String query, Object[] paramValues, int start, int pageSize) {
		return this.daoSupport.list(this.buildQueryString(query), paramValues, start, pageSize);
	}

	/* (non-Javadoc)
	 * @see whf.framework.dao.DAO#list(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> list(String query, String[] paramNames, Object[] paramValues, int start, int pageSize) {
		return this.daoSupport.list(this.buildQueryString(query), paramNames, paramValues, start, pageSize);
	}

	/**
	 * @param queryName
	 * @param params
	 * @param start
	 * @param pageSize
	 * @return
	 * @throws FindException
	 * @see whf.framework.dao.DAOSupport#getNamedQueryResult(java.lang.String, java.lang.Object[], int, int)
	 */
	public List<T> getNamedQueryResult(String queryName, Object[] params, int start, int pageSize) throws FindException {
		return daoSupport.getNamedQueryResult(queryName, params, start, pageSize);
	}

	/**
	 * @param queryName
	 * @param paramNames
	 * @param params
	 * @param start
	 * @param pageSize
	 * @return
	 * @throws FindException
	 * @see whf.framework.dao.DAOSupport#getNamedQueryResult(java.lang.String, java.lang.String[], java.lang.Object[], int, int)
	 */
	public List<T> getNamedQueryResult(String queryName, String[] paramNames, Object[] params, int start, int pageSize) throws FindException {
		return daoSupport.getNamedQueryResult(queryName, paramNames, params, start, pageSize);
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		if(this.daoSupport == null)
			this.daoSupport = new JpaDaoSupport();
	}
	
}
